package api;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class Respuesta {

	private String descripcion;
	private List<Suceso> sucesosNecesarios;
	private boolean verificaOrden;			// Indica si importa que los sucesos que observa 
											// esten en orden.	
	private boolean verificaCancelados;		// Indica si importan el estado de los eventos.
	private boolean verificaConsecutivos;	// Indica si importa que los sucesos que observa sean
											// consecutivos.
	
	public Respuesta(String descripcion, List<Suceso> sucesosNecesarios,boolean verificaOrden,boolean verificaConsecutivos,boolean verificaCancelados ) {
		this.descripcion = descripcion;
		this.sucesosNecesarios = sucesosNecesarios;
		this.verificaOrden = verificaOrden;		
		this.verificaCancelados = verificaCancelados;
		this.verificaConsecutivos = verificaConsecutivos;
	}
	
	// Para crear respuestas con un solo suceso necesario
	public Respuesta(String descripcion, Suceso sucesoNecesario) {
		this.descripcion = descripcion;
		List<Suceso> generadores = new ArrayList<Suceso>();
		generadores.add(sucesoNecesario);
		this.sucesosNecesarios = generadores;
		this.verificaOrden = false;		
		this.verificaConsecutivos = false;
		this.verificaCancelados = false;;
	}
	
	public boolean equals(Object obj){
		boolean salida = false;      
        if (obj != null && (obj.getClass().equals(this.getClass()))) {
           Respuesta respuesta = (Respuesta)obj;
           salida  = (this.descripcion.compareTo(respuesta.getDescripcion()) == 0);
       }
       return salida;
	}

	public String getDescripcion() {
		return descripcion;
	}

	public List<Suceso> getSucesosNecesarios() {
		return sucesosNecesarios;
	}

	public boolean verificaOrden() {
		return verificaOrden;
	}
		
	public boolean verificaCancelados() {
		return this.verificaCancelados;
	}
	
	public boolean verificaConsecutivos() {
		return verificaConsecutivos;
	}
	
	public boolean controlarNecesarios(){		
		API api = API.obtenerInstancia();
		List<Suceso> sucesosObservados = api.getSucesos();
		List<Suceso> necesarios = this.getSucesosNecesarios();		
		List<Integer> ordenSucesos = new ArrayList<Integer>();		
		boolean seguir = true;
		// Verifico si la cantidad de sucesos actuales alcanza para generar la respuesta
		if (necesarios.size() > sucesosObservados.size()) {			
			seguir = false;
		}
		// Verifico si alguno (o el ultimo, depende si hay orden) de los sucesos necesarios fue el ultimo observado
		else if (!verificarUltimoSucesoObservado(necesarios,this.verificaOrden())) seguir = false;
		else {
			Iterator<Suceso> itSucesosNecesarios = necesarios.iterator();			
			//Indice del suceso leido			
			int indice = 0;
			while (itSucesosNecesarios.hasNext() && seguir){
				Suceso suceso = itSucesosNecesarios.next();	
				indice = this.buscarSuceso(suceso,true);				
				if ( indice == sucesosObservados.size()) seguir = false;
				else if (this.verificaCancelados() && (sucesosObservados.get(indice).isCancelado())) {					
					seguir = false;					
				}				
				else {
					ordenSucesos.add(new Integer(indice));					
				}
			}
			
			if (seguir){
				if(this.verificaOrden()){	
					seguir = sucesosOrdenados(ordenSucesos); 
				}
				if(this.verificaConsecutivos() && seguir){
					seguir = sucesosConsecutivos(this,ordenSucesos);
				}
			}
		}
		return seguir;
	}
	
	private boolean verificarUltimoSucesoObservado(List<Suceso> necesarios,boolean requiereOrden){
		boolean seguir = false;
		API api = API.obtenerInstancia();
		List<Suceso> sucesosObservados = api.getSucesos();
		Suceso ultimoSucesoObservado = sucesosObservados.get(sucesosObservados.size()-1);
		if (requiereOrden){
			// Asigno a seguir el resultado de la comparacion entre el ultimo suceso de los observados
			// y el ultimo suceso de los necesarios.
			seguir = ultimoSucesoObservado.equals(necesarios.get(necesarios.size()-1));			
		}
		else {
			Iterator<Suceso> itNecesarios = necesarios.iterator();
			// Verifico que alguno de los necesarios este ultimo en la lista de sucesos observados
			while(itNecesarios.hasNext() && !seguir){
				Suceso suceso = itNecesarios.next();
				seguir = ultimoSucesoObservado.equals(suceso); 
			}
		}
		return seguir;
	}
	
	// Indica si los sucesos estan en el orden requerido
	private boolean sucesosOrdenados(List<Integer> ordenSucesos){
		boolean seguir = true;
		// Recorro sucesosObservados desde fin a principio
		Iterator<Integer> itOrdenSucesos = ordenSucesos.iterator();		
		Integer posicionActual = -1;
		Integer posicionProxima = 0;
		while(itOrdenSucesos.hasNext() && seguir){
			posicionProxima = itOrdenSucesos.next();
			if(posicionProxima < posicionActual) seguir = false;			
			posicionActual = posicionProxima;			
		}		
		
		return seguir;
	}
	
	// Indica si los sucesos son consecutivos
	private boolean sucesosConsecutivos(Respuesta resp, List<Integer> ordenSucesos){
		boolean seguir = true;		
		// Genero una nueva lista de orden de sucesos porque la voy a ordenar, asi no modifico la original
		List<Integer> auxOrdenSucesos = new ArrayList<Integer>();		
		auxOrdenSucesos.addAll(ordenSucesos);
		Collections.sort(auxOrdenSucesos);
		
		Iterator<Integer> itAuxOrdenSucesos = auxOrdenSucesos.iterator();		
		if (itAuxOrdenSucesos.hasNext()){			
			int posicionActual = itAuxOrdenSucesos.next();
			int posicionProxima = 0;
			while (itAuxOrdenSucesos.hasNext() && seguir){
				posicionProxima = itAuxOrdenSucesos.next();
				if ((posicionActual + 1) != posicionProxima) {
					seguir = verificarRangoCancelados(resp,posicionProxima,posicionActual);
				}				
				posicionActual = posicionProxima;
			}
		}		
		return seguir;
	}
	
	// Verifica si los sucesos entre dos posiciones estan cancelados, de ser asi devuelve verdadero
	private boolean verificarRangoCancelados(Respuesta resp, Integer nroOrdenAnterior, Integer nroOrdenActual){
		boolean seguir = true;		
		if(!resp.verificaCancelados()){
			seguir = false;
		}
		else {
			API api = API.obtenerInstancia();
			Integer indiceActual = nroOrdenAnterior;
			List<Suceso> sucesosObservados = api.getSucesos();
			// Recorro sucesosObservados desde fin a principio
			ListIterator<Suceso> itSucesosObservados = sucesosObservados.listIterator(nroOrdenAnterior);
			//(indiceActual -1) es la posicion que va a leer el previous()
			while(itSucesosObservados.hasPrevious() && (nroOrdenActual < (indiceActual-1)) && seguir){
				Suceso suceso = itSucesosObservados.previous();
				seguir = suceso.isCancelado();
				indiceActual--;
			}
		}		
		return seguir;
	}
	
	//Busca un suceso equivalente a "suceso" en la lista de sucesos observados de la api
	//Si el parametro inverso es verdadero, recorre la lista desde la ultima posicion hacia atras
	//Si existe un suceso equivalente, devuelve la posicion del suceso en la lista
	//Si no existe devuelve -1
	private int buscarSuceso(Suceso suceso, boolean inverso){
		API api = API.obtenerInstancia();
		List<Suceso> sucesosObservados = api.getSucesos();
		int encontrado = -1;
		int indice = 0;
		if(inverso){
			encontrado = sucesosObservados.size();
			indice = sucesosObservados.size()-1;			
			ListIterator<Suceso> itSucesosObservados = sucesosObservados.listIterator(sucesosObservados.size());
			while(itSucesosObservados.hasPrevious() && encontrado == sucesosObservados.size()){
				Suceso sucesoApi = itSucesosObservados.previous();
				if (suceso.equals(sucesoApi)) encontrado = indice;				
				indice--;
			}
		}
		else{
			Iterator<Suceso> itSucesosActuales = sucesosObservados.iterator();		
			while (itSucesosActuales.hasNext() && encontrado == -1){
				Suceso sucesoApi = itSucesosActuales.next();			
				if (suceso.equals(sucesoApi)) encontrado = indice;
				indice++;
			}
		}
		return encontrado;
	}
	
}
